#include "include.h"
#include "util.h"
#include "opt_manager.h"

/* XXX: TODO, likely/unlikely the branches  here */

OptManagerOptionGroup*
opt_manager_option_group_new()
{
	OptManagerOptionGroup *group = g_malloc_type(OptManagerOptionGroup);
	R_ASSERT(group);

	group->args = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
	R_ASSERT(group->args);

	group->default_opt = NULL;
	return group;
}

void
opt_manager_option_group_free(OptManagerOptionGroup *group)
{
	D_ASSERT(group);
	D_ASSERT(group->args);

	g_hash_table_destroy(group->args);
	g_free(group);
}

void
opt_manager_option_group_add_option(OptManagerOptionGroup *group, OptManagerOption *option)
{
	gchar *str;

	D_ASSERT(group);
	D_ASSERT(group->args);
	D_ASSERT(option);

	if(option->short_arg)
	{
		str = g_strconcat("-", option->short_arg, NULL);
		R_ASSERT(str);
		g_hash_table_insert(group->args, str, option);
		/* Do not g_free str here. It is automatically de-allocated by the hash_table when required to do so */
	}
	
	if(option->long_arg)
	{
		str = g_strconcat("--", option->long_arg, NULL);
		R_ASSERT(str);
		g_hash_table_insert(group->args, str, option);
		/* Do not g_free str here */
	}
	
	if(option->default_opt)
	{
		group->default_opt = option;
	}	
}

static inline gboolean is_option(const gchar *arg)
{
	D_ASSERT(arg);
	return arg[0]=='-';
}

gboolean opt_manager_parse(const OptManagerOptionGroup *opt_manager, const gint argc, const gchar *argv[])
{
	int i;

	for(i = 1; i < argc; ++i)
	{
		OptManagerOption *option = NULL;
		gchar *arg = NULL;
		const gchar *data = NULL;

		/* Investigate whether to do g_utf8_strchr in here */
		data = g_strstr_len(argv[i], -1, "=");

		if(data)
		{
			arg = strdupptr(argv[i], data);
		}
		else
		{
			arg = g_strdup(argv[i]);
		}

		/* Note: We expcet the calling program to setup its own --help argument */
		if(is_option(arg))
		{
			option = g_hash_table_lookup(opt_manager->args, arg);
		}
		else
		{
			option = opt_manager->default_opt;
		}

		if(!option)
		{
			/* We encountered some unknown option, which hasn't been registered by the calling code.
			As a result, we'll stop parsing */
			return FALSE;
		}

		if(option->args && !data)
		{
			if(i == argc-1 || is_option(argv[i+1]))
			{
				/* The user is expected to provide an arugment here, but he didn't. Idiot */
				return FALSE;
			}
			data = argv[i+1];
			++i;
		}
		else if(!option->args && data)
		{
			/* The user is not expected to provide an arugment here and he did. Moron */
			return FALSE;
		}

		option->callback(option, data);

		g_free(arg);
	}

	return TRUE;
}
